package com.qire.antscompiler;

import com.google.auto.service.AutoService;
import com.qire.antscompiler.generator.PropertyObserveCodeGenerator;
import com.qire.antscompiler.utils.Log;
import com.qire.antscore.annotation.PropertyObserve;
import com.qire.antscore.common.AssertUtils;
import com.qire.antscore.constant.ProcessorConfig;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;

/**
 * ViewModel属性观察者注解解析代码生成器，负责生成ViewModel属性观察者绑定注入及解绑注销等行为的相关代码
 */
@AutoService(Processor.class)
public class PropertyObserveProcessor extends AbstractProcessor {

    // 日志工具
    private Log log;
    // 代码生成器
    private PropertyObserveCodeGenerator codeGenerator;

    /**
     * 初始化注解处理器
     * @param processingEnv 处理器环境，可以从中获得一些工具类和环境配置
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        // 获得apt的日志输出
        log = Log.newLog(processingEnv.getMessager());

        // 用于build类中组成类名防止在多模块情况下冲突
        String moduleName = null;
        // 参数是模块名 为了防止多模块/组件化开发的时候 生成相同的 xx$$ROOT$$文件
        Map<String, String> options = processingEnv.getOptions();

        if (AssertUtils.notEmpty(options)) {
            moduleName = options.get(ProcessorConfig.OPTIONS_MODULE_NAME);
        }
        if (AssertUtils.isEmpty(moduleName)) {
            String exceptionMsg = new StringBuilder()
                    .append("未设置处理器 ").append(ProcessorConfig.OPTIONS_MODULE_NAME).append(" 选项!")
                    .append("请检查所有子模块的 build.gradle 是否设置:")
                    .append("javaCompileOptions.annotationProcessorOptions.arguments = [")
                    .append(ProcessorConfig.OPTIONS_MODULE_NAME)
                    .append(": project.getName()]").toString();
            throw new RuntimeException(exceptionMsg);
        }
        codeGenerator = new PropertyObserveCodeGenerator(processingEnv);
        log.i("init ObserveBinderProcessor " + moduleName + " success !");
    }

    /**
     * 返回该处理器识别的选项。
     * Processor处理工具类想使用getOptions获取外部传入选项，必须实现该方法来区分 传递给工具本身的选项 中 传递给特定处理器的选项，
     * 传递给工具的特定处理器的选项:命令行可以实现 传递区分特定处理器的选项，如 javac -A ,用已知字符串(如"-A") 为它们添加前缀。
     * 参见{@link ProcessingEnvironment#getOptions getOptions}。
     * 使用gradle构建，可以通过配置以下内容，来指定执行编译命令行时传递的外部参数。
     * javaCompileOptions {
     *      annotationProcessorOptions {
     *          arguments = [moduleName: project.getName()]
     *      }
     * }
     * 在集合中返回的每个字符串必须是一个以句点分隔的标识符序列:
     * @return 此处理器识别的选项，如果没有，则为空集合
     * 另请参阅:
     * 如果想使用注解代替，请看{@link SupportedOptions}注释，使用注解后super.getSupportedOptions()会获得一个不可更改的Set
     */
    @Override
    public Set<String> getSupportedOptions() {
//        return super.getSupportedOptions();
        HashSet<String> options = new LinkedHashSet<>();
        options.add(ProcessorConfig.OPTIONS_MODULE_NAME);
        return options;
    }

    /**
     * 指定这个注解处理器是注册给哪个注解的，这里说明是注解 PropertyObserve
     * @return 返回一个包含处理器关注的注解清单，如果扫描中未发现注解清单中的注解则不会执行process()注解处理过程。
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        HashSet<String> supportTypes = new LinkedHashSet<>();
        supportTypes.add(PropertyObserve.class.getCanonicalName());
        return supportTypes;
    }

    /**
     * 指定使用的Java版本
     * @return 使用的java版本号，如果该参数未指定则会提示警告，有可能导致未知错误。SourceVersion.latestSupported()表示支持到最新
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * 注解处理过程，如果扫描发现存在关注的注解清单里的注解则会调用该处理过程。
     * @param annotations 使用了支持处理注解的节点集合
     * @param roundEnv  表示当前或是之前的运行环境,可以通过该对象查找找到的注解，
     *                  注：改命名为轮环境猜测可能和Apt工作机制有关，一旦发现关注注解，APT会反复进行注解器连询问直到有处理为止。
     * @return 注解处理器并不是唯一的处理器，允许存在多个，且可能为链式注册调用，如果返回值为true则表示该注解已被处理过，不希望后续处理器处理，
     * 如果为false，则表示后续处理器可以处理。
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (AssertUtils.notEmpty(annotations)) {
            //被Route注解的节点集合
            Set<? extends Element> rootElements = roundEnv.getElementsAnnotatedWith(PropertyObserve.class);
            if (AssertUtils.notEmpty(rootElements)) {
                codeGenerator.parsePropertyObserve(rootElements);
            }
            return true;
        }
        return false;
    }

}
